﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
using SweeperPrototype.Business.Fields;
using SweeperPrototype.Business.Keys;
using SweeperPrototype.Objects;
using Timer = System.Timers.Timer;

namespace SweeperPrototype.Business.Plumbing.Requests
{

    public interface IPositionRequestManager
    {
        NotifyList<PositionSubscription> Subscribe(BookCode key);
    }

    public delegate void Notify<T>(T update);

    public class PositionRequestManager : IPositionRequestManager, IDisposable
    {
        private class Request
        {
            public readonly BookCode Key;
            public readonly Notify<PositionSubscription.PositionUpdate> Update;
            public Request(PositionSubscription.UniqueKey key, Notify<PositionSubscription.PositionUpdate> update)
            {
                Update = update;
            }
        }


        private IPositionSource m_Source;
        public PositionRequestManager( IPositionSource source )
        {
            m_Source = source;
            m_BookRequests = new ConcurrentDictionary<BookCode, NotifyList<PositionSubscription>>();

            m_Timer = new System.Threading.Timer(delegate(object state)
                                                     {
                                                         HandleTimerElapsed();
                                                     });
            m_Timer.Change(200, 5000);
        }

        private readonly System.Threading.Timer m_Timer;

        private readonly ConcurrentDictionary<BookCode, NotifyList<PositionSubscription>> m_BookRequests;
        public NotifyList<PositionSubscription> Subscribe(BookCode key )
        {
            NotifyList<PositionSubscription> list;
            if (m_BookRequests.TryGetValue(key, out list) == false )
            {
                list = new NotifyList<PositionSubscription>();
                while (m_BookRequests.TryAdd(key, list) == false) { }
            }
            return list;
        }

        private void HandleTimerElapsed()//object sender, ElapsedEventArgs elapsed_event_args)
        {
            foreach( BookCode code in m_BookRequests.Keys )
            {
                NotifyList<PositionSubscription> subscriptions;
                while( m_BookRequests.TryGetValue( code, out subscriptions ) == false ){}
                Console.WriteLine(subscriptions.Count.ToString());

                List<PositionSubscription> contained = new List<PositionSubscription>();
                contained.AddRange( subscriptions.Items );
                List<PositionSubscription> list = m_Source.FetchPositions(code); 
                
                foreach (PositionSubscription p in list)
                {
                    PositionSubscription.PositionUpdate update = null;
                    foreach (PositionSubscription contained_p in contained)
                    {
                        if (p.RIC == contained_p.RIC && p.BookCode == contained_p.BookCode)
                        {
                            update = new PositionSubscription.PositionUpdate(p.Quantity, 
                                new Timestamp(DateTime.Now));
                            contained_p.Update(update);
                            // TODO: should use indexer
                            if( contained.Remove(contained_p) == false )
                            {
                                throw new Exception("err?");
                            }
                            break;
                        }
                    }
                    // we didn't find it
                    if( update == null )
                    {
                        subscriptions.Add( p );
                    }
                    Thread.Sleep(1);
                }
                // remove remainder
                foreach( PositionSubscription contained_p in contained )
                {
                    subscriptions.Remove( contained_p );
                }
            }
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            m_Timer.Dispose();
        }

        #endregion

    }
}
